package net.dubboclub.maven;


import net.dubboclub.maven.clean.CleanMojo;
import net.dubboclub.maven.compiler.CompilationFailureException;
import net.dubboclub.maven.compiler.CompilerMojo;
import net.dubboclub.maven.jar.JarMojo;
import net.dubboclub.maven.resources.ResourcesMojo;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.compressors.CompressorException;
import org.apache.commons.compress.compressors.CompressorOutputStream;
import org.apache.commons.compress.compressors.CompressorStreamFactory;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.artifact.install.ArtifactInstaller;
import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
import org.apache.maven.shared.dependency.graph.DependencyNode;

import java.io.*;
import java.nio.channels.FileChannel;
import java.util.List;




public abstract class AbstractDubbomMojo extends CleanMojo {

    @Parameter(defaultValue = "${project}",readonly = true,required = true)
    protected MavenProject mavenProject;


    @Parameter(readonly=true, required=true, defaultValue="${localRepository}")
    protected ArtifactRepository localRepository;

    @Component
    protected ArtifactResolver artifactResolver;

    @Parameter(defaultValue = "${project.build.directory}/${project.artifactId}",required = true)
    protected File targetDir;
    @Parameter(defaultValue = "${project.build.directory}",required = true,readonly = true)
    protected File buildDir;


    @Parameter(readonly=true, required=true, defaultValue="${project.remoteArtifactRepositories}")
    protected List<ArtifactRepository> remoteArtifactRepositories;
    @Component
    protected DependencyGraphBuilder dependencyGraphBuilder;

    @Component
    protected ArtifactFactory artifactFactory;






    private static final String DUBBOM_GROUPID="net.dubboclub";

    private static final String DUBBOM_ARTIFACTID="DubboM-all";

    protected void pack(boolean generateTar) throws MojoExecutionException, IOException {
        //clean();
        try {
            getLog().info("Starting clean.....");
            clean();
            getLog().info("Finished clean.....");
            getLog().info("Starting compiler.....");
            resources();
            compiler();
            getLog().info("Finished compiler");
            getLog().info("Starting package jar....");
            jar();
            getLog().info("Finished package jar....");
        } catch (CompilationFailureException e) {
            throw new MojoExecutionException("Fail to pack",e);
        }
        try{
            getLog().info("Starting pack project "+mavenProject.getGroupId()+":"+mavenProject.getArtifactId());
            DependencyNode dependencyNode= dependencyGraphBuilder.buildDependencyGraph(mavenProject, new ArtifactFilter() {

                public boolean include(Artifact artifact) {
                    if(artifact.getGroupId().equals(DUBBOM_GROUPID)&&artifact.getArtifactId().equals(DUBBOM_ARTIFACTID)){
                        return false;
                    }
                    return true;
                }
            });
            getLog().info(targetDir.getPath());
            if(targetDir.exists()){
                deleteFile(targetDir);
            }else{
                targetDir.mkdirs();
            }
            recursiveDependency(dependencyNode, 1);
            File jarFile = new File(buildDir,mavenProject.getArtifactId()+"-"+mavenProject.getVersion()+".jar");
            copyDependencyToTarget(jarFile);
            if(generateTar){
                generateTar();
            }
            getLog().info("Pack project "+mavenProject.getGroupId()+":"+mavenProject.getArtifactId()+" success.");
        }catch (Exception e){
            throw new MojoExecutionException("Fail to pack dubbom.",e);
        }
    }

    private void generateTar() throws Exception {
        File[] files = targetDir.listFiles();
        File tarFile = new File(targetOutputDirectory,targetDir.getName()+".tar.gz");
        CompressorOutputStream gzippedOut = new CompressorStreamFactory()
                .createCompressorOutputStream(CompressorStreamFactory.GZIP, new FileOutputStream(tarFile));
        TarArchiveOutputStream tarArchiveOutputStream = new TarArchiveOutputStream(gzippedOut);
        try {
            for(File file:files){
                TarArchiveEntry tarArchiveEntry = new TarArchiveEntry(file,file.getName());
                tarArchiveOutputStream.putArchiveEntry(tarArchiveEntry);
                tarArchiveOutputStream.write(readFileBytes(file));
                tarArchiveOutputStream.closeArchiveEntry();
            }
        } catch (IOException e) {
            throw new MojoExecutionException("Fail to pack tar file.",e);
        }finally {
            tarArchiveOutputStream.flush();
            tarArchiveOutputStream.close();
            gzippedOut.flush();
            gzippedOut.close();
        }
    }

    private byte[] readFileBytes(File file) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(file);
        try{
            byte[] bytes = IOUtils.toByteArray(fileInputStream);
            return bytes;
        }finally {
            org.apache.commons.io.IOUtils.closeQuietly(fileInputStream);
        }
    }

    private void recursiveDependency(DependencyNode dependencyNode,int level) throws IOException, ArtifactNotFoundException, ArtifactResolutionException {
        List<DependencyNode> dependencyNodes = dependencyNode.getChildren();
        for(DependencyNode child:dependencyNodes){
            copyDependencyToTarget(artifactResolver(child.getArtifact()).getFile());
            //getLog().info(logBlankspace(level) + child.getArtifact().getGroupId() + ":" + child.getArtifact().getArtifactId() + ":" + child.getArtifact().getVersion());
            recursiveDependency(child,level+1);
        }
    }

    private Artifact artifactResolver(Artifact artifact) throws ArtifactNotFoundException, ArtifactResolutionException {
        Artifact resolvedArtifact = artifactFactory.createArtifactWithClassifier(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), "jar", "");
        artifactResolver.resolve(resolvedArtifact,remoteArtifactRepositories,localRepository);
        getLog().info(resolvedArtifact.getFile().getPath()+"");
        return resolvedArtifact;
    }

    private void deleteFile(File file){
        if(file.isDirectory()){
            File[] files = file.listFiles();
            for(File child:files){
                deleteFile(child);
            }
        }else{
            file.delete();
        }
    }



    private void copyDependencyToTarget(File file) throws IOException {
        File targetFile = new File(targetDir,file.getName());
        FileChannel reader =null;
        FileChannel writer =null;
        try{
            reader = new FileInputStream(file).getChannel();
            writer = new FileOutputStream(targetFile).getChannel();
            writer.transferFrom(reader,0,reader.size());
        }finally {
            if(reader!=null){
                reader.close();
            }
            if(writer!=null){
                writer.force(true);
                writer.close();
            }
        }
    }
}
